#!/usr/bin/
# -*- coding: utf-8 -*-
import os
import sys
import random
import subprocess
import webbrowser
import cv2.cv2 as cv
from PyQt5.QtCore import Qt, QDate, QTimer, QRect, QLine, pyqtSignal
from PyQt5.QtGui import QPixmap, QPainter, QPen, QColor, QIcon, QPalette, QImage, QBrush, QTextCursor
from PyQt5.QtWidgets import QLabel, QMainWindow, QLCDNumber, QTableWidgetItem, QMessageBox, QFileDialog
from PyQt5.QtWidgets import QHeaderView, QAbstractItemView, QDialog
from PyQt5.uic import loadUiType
from sympy import symbols, Eq, solve
import DataBase
import setup_map
import setup_sql
import aboutDialog
import contact
import point_attribute

use_database = True
map_show = True
mysql_message = [['127.0.0.1', 3306, 'root', 'lucky123', 'traffic_manager', 'utf8']]
map_mode = "realtime_condition"
map_nodes = []
map_roads = []
map_threshold = []
road_width = 3
road_crowd_data = []
road_flow_data = []
database_index = 0
area_cross = {}
area_cross_phase = {}
area_cross_road = {}

# ui文件路径
ui, _ = loadUiType("resource/ui/interface.ui")


# 重写QLabel类，作为地图画布
class MyLabel(QLabel):
    interInfoEmit = pyqtSignal(str)
    roadInfoEmit = pyqtSignal(list)
    start_point = False
    end_point = True
    update_timer = False
    line_width = road_width
    num = 0
    x0, x1, x2 = 0, 0, 0
    y0, y1, y2 = 0, 0, 0
    start_node, dest_node = 0, 0
    nodes, roads = [], []
    road_info = []
    operation_record = []
    crowded_level = [
        [0, 255, 0],    # 绿色
        [255, 255, 0],  # 黄色
        [255, 0, 0],    # 红色
        [165, 42, 42],  # 褐色
    ]

    # 求解方程组，得到两条平行线
    @staticmethod
    def solve_new_line(a, b, c, d, e):
        x1, y1, x2, y2 = symbols('x1 y1 x2 y2')
        eqs = [
            Eq((y1 - b) ** 2 + (x1 - a) ** 2, e ** 2),
            Eq((y1 - b) * (d - b), (a - c) * (x1 - a)),
            Eq((y2 - d) ** 2 + (x2 - c) ** 2, e ** 2),
            Eq((y2 - d) * (d - b), (a - c) * (x2 - c)),
            Eq((y2 - y1) * (c - a), (d - b) * (x2 - x1)),
        ]
        result = solve(eqs, [x1, y1, x2, y2])
        p0, p1 = result[0], result[1]
        result[0] = list(map(round, [p0[0], p0[1], p0[2], p0[3]]))
        result[1] = list(map(round, [p1[2], p1[3], p1[0], p1[1]]))
        return result

    # 选点模式下鼠标左键按下
    def left_mousePress(self, event):
        x, y = event.x(), event.y()
        pa = point_attribute.point_attribute()
        result = pa.exec_()
        if result == QDialog.Accepted:
            is_cross, area_num, cross_num, phase_amount = pa.attribute
            if is_cross:
                info = "道路选点->鼠标左键按下，选中交叉口\n选中坐标: ({:<3d}, {:>3d})\n分配归属: 区域{} 路口{} 相位数目{}". \
                    format(x, y, area_num, cross_num, phase_amount)
            else:
                info = "道路选点->鼠标左键按下，选中普通点位\n选中坐标: ({:<3d}, {:>3d})\n". \
                    format(x, y)
            self.interInfoEmit.emit(info)
            self.num += 1
            self.x0, self.y0 = x, y
            node = [self.x0, self.y0, is_cross, area_num, cross_num, phase_amount]
            self.nodes.append(node)
            # 添加操作记录
            self.operation_record.append([0, node])
            # 立即更新绘图
            self.update()
        elif result == QDialog.Rejected:
            info = "道路选点->鼠标左键按下，未确认\n选中坐标: ({:<3d}, {:>3d})".format(x, y)
            self.interInfoEmit.emit(info)

    # 选点模式下鼠标右键按下
    def right_mousePress(self, event):
        x, y = event.x(), event.y()
        # 已知选点超过2个，可以右键开始连接道路,否则无效
        if self.num >= 2:
            # 上次终点已按下并确认，当前起点未按下
            if self.end_point and (not self.start_point):
                # 获取点击坐标
                self.x1, self.y1 = x, y
                # 终点置0
                self.x2, self.y2 = 0, 0
                find = False
                for i in range(len(self.nodes)):
                    condition1 = (abs(self.nodes[i][0] - self.x1) < 10)
                    condition2 = (abs(self.nodes[i][1] - self.y1) < 10)
                    if condition1 and condition2:
                        find = True
                        # 修正起点为已存在的结点
                        self.x1, self.y1 = self.nodes[i][0], self.nodes[i][1]
                        self.start_node = i + 1
                        info = "道路选点->鼠标右键按下，选中起点\n起点坐标: ({:<3d}, {:>3d})".format(self.x1, self.y1)
                        self.interInfoEmit.emit(info)
                        # 设置起点已按下并确认，终点未按下，
                        self.start_point = True
                        self.end_point = False
                        # 添加操作记录
                        self.operation_record.append([1, [self.x1, self.y1]])
                        # 立即更新绘图
                        self.update()
                        break
                # 若选中的为无效点，本次起点设置无效
                if not find:
                    self.start_point = False
                    self.end_point = True
                    self.x1, self.y1 = 0, 0
                    info = "道路选点->鼠标右键按下，未选中已有路口\n选中坐标: ({:<3d}, {:>3d})".format(x, y)
                    self.interInfoEmit.emit(info)
            # 当前起点已按下并确认，当前终点未按下
            elif self.start_point and (not self.end_point):
                self.x2, self.y2 = x, y
                find = False
                for i in range(len(self.nodes)):
                    condition1 = (abs(self.nodes[i][0] - self.x2) < 10)
                    condition2 = (abs(self.nodes[i][1] - self.y2) < 10)
                    condition3 = (self.nodes[i][0] != self.x1)
                    condition4 = (self.nodes[i][1] != self.y1)
                    if (not condition3) and (not condition4):
                        info = "道路选点->鼠标右键按下，取消起点\n起点坐标: ({:<3d}, {:>3d})".format(self.x1, self.y1)
                        self.interInfoEmit.emit(info)
                    if condition1 and condition2 and condition3 and condition4:
                        find = True
                        # 确认终点
                        self.x2, self.y2 = self.nodes[i][0], self.nodes[i][1]
                        self.dest_node = i + 1
                        info = "道路选点->鼠标右键按下，选中终点\n终点坐标: ({:<3d}, {:>3d})".format(self.x2, self.y2)
                        self.interInfoEmit.emit(info)
                        reply = QMessageBox.question(self, '道路方向', '设置为双向道路', QMessageBox.Yes | QMessageBox.No,
                                                     QMessageBox.Yes)
                        if reply == QMessageBox.No:
                            # 添加单向道路
                            sn, dn = self.start_node, self.dest_node
                            road = [self.x1, self.y1, self.x2, self.y2, sn, dn]
                            map_roads.append(road)
                            info = "道路选点->鼠标右键按下，选中终点->单向道路\n起点坐标: ({:<3d}, {:>3d})\n终点坐标: ({:<3d}, {:>3d})". \
                                format(self.x1, self.y1, self.x2, self.y2)
                            self.interInfoEmit.emit(info)
                            info = "道路{:<2d} 起点:({:<3d},{:>3d}) → 终点:({:<3d},{:>3d})". \
                                format(len(self.road_info) + 1, self.x1, self.y1, self.x2, self.y2)
                            self.road_info.append(info)
                            self.roadInfoEmit.emit(self.road_info)
                            # 添加操作记录
                            self.operation_record.append([2, road])
                        elif reply == QMessageBox.Yes:
                            # 添加双向道路
                            new_line = self.solve_new_line(self.x1, self.y1, self.x2, self.y2, 5)
                            L1, L2 = new_line[0], new_line[1]
                            sn, dn = self.start_node, self.dest_node
                            road_1 = [int(L1[0]), int(L1[1]), int(L1[2]), int(L1[3]), sn, dn]
                            road_2 = [int(L2[0]), int(L2[1]), int(L2[2]), int(L2[3]), dn, sn]
                            map_roads.append(road_1)
                            map_roads.append(road_2)
                            info = "道路选点->鼠标右键按下，选中终点->双向道路\n起点坐标: ({:<3d}, {:>3d})\n终点坐标: ({:<3d}, {:>3d})". \
                                format(self.x1, self.y1, self.x2, self.y2)
                            self.interInfoEmit.emit(info)
                            info = "道路{:<2d} 起点:({:<3d},{:>3d}) → 终点:({:<3d},{:>3d})". \
                                format(len(self.road_info) + 1, int(L1[0]), int(L1[1]), int(L1[2]), int(L1[3]))
                            self.road_info.append(info)
                            info = "道路{:<2d} 起点:({:<3d},{:>3d}) → 终点:({:<3d},{:>3d})". \
                                format(len(self.road_info) + 1, int(L2[0]), int(L2[1]), int(L2[2]), int(L2[3]))
                            self.road_info.append(info)
                            self.roadInfoEmit.emit(self.road_info)
                            # 添加操作记录
                            self.operation_record.append([3, road_1, road_2])
                        # 设置一下次起点未按下,终点已按下并确认
                        self.start_point = False
                        self.end_point = True
                        # 立即更新绘图
                        self.update()
                        break
                # 若选中的为无效点，本次终点设置无效，起点无效，道路无效
                if not find:
                    self.start_point = False
                    self.end_point = True
                    self.x2, self.y2 = 0, 0
                    self.x1, self.y1 = 0, 0
        else:
            info = "道路选点->鼠标右键按下，无效操作\n少于两个路口，无法连接道路\n选中坐标: ({:<3d}, {:>3d})".format(x, y)
            self.interInfoEmit.emit(info)

    # 实时路况绘制
    def realtime_condition_paint(self):
        painter = QPainter(self)
        painter.begin(self)
        # 显示道路
        for i in range(len(self.roads)):
            # QLine(起点x，起点y，终点x，终点y)，坐标数值均为int型
            line = QLine(self.roads[i][-4], self.roads[i][-3], self.roads[i][-2], self.roads[i][-1])
            try:
                score = road_crowd_data[i]
            except (IndexError, Exception):
                score = 0
            color = self.crowded_level[score]
            R, G, B = color[0], color[1], color[2]
            painter.setPen(QPen(QColor(R, G, B), self.line_width, Qt.SolidLine))
            painter.drawLine(line)
            # painter.setPen(QPen(Qt.black, self.line_width, Qt.SolidLine))
            # m = (self.roads[i][-4] + self.roads[i][-2]) // 2
            # n = (self.roads[i][-3] + self.roads[i][-1]) // 2
            # painter.drawText(m, n, str(i + 1))
        # 显示道路结点
        cross_num = 1
        for i in range(len(self.nodes)):
            # 交叉口点位
            if self.nodes[i][2]:
                rect = QRect(self.nodes[i][-2] - 4, self.nodes[i][-1] - 4, 8, 8)
                painter.setPen(QPen(Qt.black, self.line_width, Qt.SolidLine))
                painter.fillRect(rect, Qt.blue)
                painter.drawText(self.nodes[i][-2] - 8, self.nodes[i][-1] - 8, str(cross_num))
                cross_num += 1
        painter.end()

    # 道路选点绘制
    def choose_point_paint(self):
        painter = QPainter(self)
        painter.begin(self)
        # 画出已存在的道路
        for i in range(len(self.roads)):
            coordinate = self.roads[i]
            line = QLine(coordinate[0], coordinate[1], coordinate[2], coordinate[3])
            painter.setPen(QPen(Qt.red, self.line_width, Qt.SolidLine))
            painter.drawLine(line)
        # 画出已存在的点
        if len(self.nodes) != 0:
            cross_num = 1
            for i in range(len(self.nodes)):
                condition1 = self.x1 != 0 and self.y1 != 0
                condition2 = self.x2 == 0 and self.y2 == 0
                condition3 = (self.nodes[i][0] == self.x1) and (self.nodes[i][1] == self.y1)
                # 刚选中一条新的道路起点，着重突出该点
                if condition1 and condition2 and condition3:
                    rect = QRect(self.x1 - 6, self.y1 - 6, 12, 12)
                    painter.setPen(QPen(Qt.white, self.line_width, Qt.SolidLine))
                    painter.fillRect(rect, Qt.white)
                # 其他点原样画出
                else:
                    rect = QRect(self.nodes[i][0] - 4, self.nodes[i][1] - 4, 8, 8)
                    painter.setPen(QPen(Qt.black, self.line_width, Qt.SolidLine))
                    # 交叉口点位
                    if self.nodes[i][2]:
                        painter.fillRect(rect, Qt.blue)
                        painter.drawText(self.nodes[i][0] - 8, self.nodes[i][1] - 8, str(cross_num))
                        cross_num += 1
                    # 普通点位
                    else:
                        painter.fillRect(rect, Qt.black)
        painter.end()

    # 鼠标点击事件
    def mousePressEvent(self, event):
        QLabel.mousePressEvent(self, event)
        # 选点模式下鼠标点击事件
        if map_mode == "choose_point":
            # 鼠标左键按下
            if event.buttons() == Qt.LeftButton:
                self.left_mousePress(event)
            # 鼠标右键按下
            elif event.buttons() == Qt.RightButton:
                self.right_mousePress(event)

    # 电子地图界面绘制事件
    def paintEvent(self, event):
        # 注：第一次实例化MyLabel类时，会自动执行一次鼠标绘制事件
        super().paintEvent(event)
        # 显示地图界面
        if map_show:
            global map_nodes, map_roads
            # 设置定时刷新
            if not self.update_timer:
                self.update_timer = True
                timer = QTimer(self)
                timer.start(1000)
                timer.timeout.connect(self.update)
            self.nodes = map_nodes
            self.roads = map_roads
            # 地图模式为实时路况
            if map_mode == "realtime_condition":
                self.realtime_condition_paint()
            # 地图模式为道路选点
            elif map_mode == "choose_point":
                self.choose_point_paint()


# 重写QLabel类，作为信号灯界面地图画布
class MyLabel_light(QLabel):
    location_x, location_y = 0, 0
    nodes = []

    # 信号配时界面绘制事件
    def paintEvent(self, event):
        super().paintEvent(event)
        self.nodes = map_nodes
        painter = QPainter(self)
        painter.begin(self)
        # 显示道路结点
        cross_num = 1
        for i in range(len(self.nodes)):
            # 交叉口点位
            if self.nodes[i][2]:
                rect = QRect(self.nodes[i][-2] - 4, self.nodes[i][-1] - 4, 8, 8)
                painter.fillRect(rect, Qt.blue)
                painter.setPen(QPen(Qt.black, 2, Qt.SolidLine))
                painter.drawText(self.nodes[i][-2] - 8, self.nodes[i][-1] - 8, str(cross_num))
                cross_num += 1
        # 显示定位点
        if self.location_x != 0 and self.location_y != 0:
            painter.drawPixmap(self.location_x - 15, self.location_y - 30, QPixmap("resource/icon/location1.png"))
        painter.end()


# 主窗口界面
class MainWindow(QMainWindow, ui):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setupUi(self)
        self.resize(1280, 690)
        self.setWindowTitle("城市道路智慧交通管理系统")
        self.setWindowIcon(QIcon("resource/icon/icon.png"))  # 设置应用图标
        self.setFixedSize(self.width(), self.height())  # 禁止拉伸窗口大小
        self.setup_map_ui = setup_map.Setup_Map()
        self.setup_sql_ui = setup_sql.Setup_Sql()
        self.versionDialog = aboutDialog.versionDialog()
        self.contactDialog = contact.contactDialog()
        self.DB = DataBase.DataBase()
        self.timer_map = QTimer(self)  # 定义电子地图界面的时间刷新定时器
        self.timer_light = QTimer(self)  # 定义信号配时界面的倒计时定时器
        self.timer_video = QTimer(self)  # 视频播放定时器
        self.map_label = MyLabel(self.label_map)  # 重写label_map控件
        self.map_light_label = MyLabel_light(self.label_map_light)
        self.undo_operation_record = []
        self.qss_style = []  # 信号灯样式表
        self.count_down_time = [10, 10, 10]  # 默认倒计时
        self.pre_count_down_time = [10, 10, 10]  # 默认预测配时
        self.cap = cv.VideoCapture("./resource/video/192.168.100.219.mp4")
        self.map_bg = "./resource/map/BG1.png"
        self.init_setup()

    # 控件初始化设置
    def init_setup(self):
        # 主界面界面切换
        self.pushButton1.clicked.connect(self.switch_to_map_panel)
        self.pushButton2.clicked.connect(self.switch_to_light_time_panel)
        self.pushButton3.clicked.connect(self.switch_to_traffic_state_panel)
        self.pushButton4.clicked.connect(self.switch_to_work_state_panel)
        # 菜单栏快捷键设置
        self.action_import.setShortcut('Ctrl+I')
        self.action_export.setShortcut('Ctrl+E')
        self.action_quit.setShortcut('Ctrl+Q')
        self.action_setup_sql.setShortcut('Ctrl+D')
        self.action_setup_map.setShortcut('Ctrl+M')
        self.action_local.setShortcut('Ctrl+H')
        self.action_online.setShortcut('Ctrl+O')
        self.action_version.setShortcut('Ctrl+V')
        self.action_contact.setShortcut('Ctrl+C')
        # 菜单栏触发信号
        self.action_import.triggered.connect(self.import_data)
        self.action_export.triggered.connect(self.export_data)
        self.action_quit.triggered.connect(self.close)
        self.action_setup_map.triggered.connect(self.setup_map_ui.show)
        self.action_setup_sql.triggered.connect(self.setup_sql_ui.show)
        self.action_local.triggered.connect(self.open_local_help_chm)
        self.action_online.triggered.connect(self.open_online_help_network)
        self.action_version.triggered.connect(self.versionDialog.show)
        self.action_contact.triggered.connect(self.contactDialog.show)
        # 自定义信号槽函数连接
        self.setup_map_ui.mapInfoEmit.connect(self.set_map_info)
        self.setup_sql_ui.sqlInfoEmit.connect(self.set_sql_info)
        self.map_label.interInfoEmit.connect(self.show_inter_info)
        self.map_label.roadInfoEmit.connect(self.show_road_info)
        # 隐藏tab标签
        self.tabWidget.tabBar().setVisible(False)
        self.init_map()       # 初始化地图界面
        self.init_light()     # 初始化信号灯配时界面
        self.init_status()    # 初始化状态界面
        self.switch_to_map_panel()  # 默认显示电子地图界面

    # 初始化地图界面
    def init_map(self):
        # 设置控件区域鼠标的光标形状（默认为箭头）
        self.map_label.setCursor(Qt.ArrowCursor)
        # 设置道路选点模式下按钮可见
        self.groupBox_choose.setVisible(True)
        # 设置按钮可用状态
        self.pushButton_choose.setText("道路选点")
        self.pushButton_choose.setEnabled(True)
        self.pushButton_clear_all.setEnabled(False)
        self.pushButton_do.setEnabled(False)
        self.pushButton_undo.setEnabled(False)
        # 设置按钮图标
        self.pushButton_choose.setIcon(QIcon(QPixmap("./resource/icon/switch.png")))
        self.pushButton_clear_all.setIcon(QIcon(QPixmap("./resource/icon/clear.png")))
        self.pushButton_do.setIcon(QIcon(QPixmap("./resource/icon/do.png")))
        self.pushButton_undo.setIcon(QIcon(QPixmap("./resource/icon/undo.png")))
        self.pushButton_play.setIcon(QIcon(QPixmap("./resource/icon/play.png")))
        # 让图片自适应label大小
        self.map_label.setScaledContents(True)
        # 显示交互提示信息
        self.textBrowser_inter.clear()
        self.textBrowser_inter.append("实时路况模式：\n鼠标左键按下查看坐标\n鼠标右键按下查看坐标")
        # 槽函数连接
        self.timer_map.timeout.connect(self.show_road_condition)
        self.pushButton_play.clicked.connect(self.play_video)
        self.timer_video.timeout.connect(self.show_pic)
        self.pushButton_choose.clicked.connect(self.switch_map_mode)
        self.pushButton_clear_all.clicked.connect(self.clear_all_choose)
        self.pushButton_do.clicked.connect(self.do_operation)
        self.pushButton_undo.clicked.connect(self.undo_operation)
        self.map_canvas()            # 设置地图画布
        self.map_mode()              # 设置地图模式
        self.show_road_condition()   # 初始化道路状态

    # 播放视频设置
    def play_video(self):
        if self.pushButton_play.text() == "播放":
            self.pushButton_play.setText("关闭")
            self.pushButton_play.setIcon(QIcon(QPixmap("./resource/icon/shutdown.png")))
            self.timer_video.start(int(1000 / self.cap.get(cv.CAP_PROP_FPS)))
        else:
            self.pushButton_play.setText("播放")
            self.pushButton_play.setIcon(QIcon(QPixmap("./resource/icon/play.png")))
            self.timer_video.stop()
            self.label_video.setText("路口网络摄像头实时画面")

    # 显示视频
    def show_pic(self):
        success, frame = self.cap.read()
        if success:
            frame = cv.resize(frame, (480, 320))
            show = cv.cvtColor(frame, cv.COLOR_BGR2RGB)
            show_image = QImage(show.data, show.shape[1], show.shape[0], QImage.Format_RGB888)
            self.label_video.setPixmap(QPixmap.fromImage(show_image))

    # 导入数据
    def import_data(self):
        global map_nodes, map_roads
        self.textBrowser_inter.clear()
        info = "导入数据！"
        self.textBrowser_inter.append(info)
        reply = QFileDialog.getOpenFileName(self, '导入文件', './resource/data/data.txt', "文本(*.txt)")
        try:
            if reply[0]:
                # 从文件中读取数据
                with open(reply[0], "r", encoding="utf-8") as f:
                    lines = f.readlines()  # 读行
                nodes = []
                roads = []
                cnt = 0
                for i in range(len(lines)):
                    line = lines[i].split()
                    if line[0] == "id":
                        cnt += 1
                    else:
                        if cnt == 1:
                            nodes.append(list(map(int, line)))
                        elif cnt == 2:
                            roads.append(list(map(int, line)))
                if nodes and roads:
                    map_nodes = nodes
                    map_roads = roads
                    QMessageBox.about(self, '提示', '导入数据成功')
                    self.textBrowser_inter.clear()
                    info = "导入数据成功，导入文件为{}".format(reply[0])
                    self.textBrowser_inter.append(info)
                else:
                    QMessageBox.about(self, '提示', '导入数据失败，因为文件内容不符合规范')
                    self.textBrowser_inter.clear()
                    info = "导入数据失败，选择的导入文件为{}".format(reply[0])
                    self.textBrowser_inter.append(info)
            else:
                QMessageBox.about(self, '提示', '导入数据失败，因为你未选中一个有效的文件')
                self.textBrowser_inter.clear()
                info = "数据导入失败！"
                self.textBrowser_inter.append(info)
        except (IndexError, Exception):
            pass

    # 导出选点数据
    def export_data(self):
        global map_nodes, map_roads
        if map_mode == "choose_point":
            if map_roads or map_nodes:
                reply = QFileDialog.getSaveFileName(self, '保存文件', './resource/data/data.txt', "文本(*.txt)")
                try:
                    if reply[0]:
                        content = ""
                        # 开始保存nodes
                        lines = "id" + "\t\t" + "is" + "\t\t" + "ar" + "\t\t" + "cr" + "\t\t" + \
                                "ph" + "\t\t" + "x" + "\t\t" + "y" + "\n"
                        content += lines
                        for row, line in enumerate(map_nodes):
                            lines = str(row + 1) + "\t\t" + str(line[2]) + "\t\t" + str(line[3]) + "\t\t" + \
                                    str(line[4]) + "\t\t" + str(line[5]) + "\t\t" + str(line[0]) + "\t\t" + \
                                    str(line[1]) + "\n"
                            content += lines
                        # 开始保存roads
                        lines = "id" + "\t\t" + "sn" + "\t\t" + "dn" + "\t\t" + "sx" + "\t\t" + \
                                "sy" + "\t\t" + "dx" + "\t\t" + "dy" + "\n"
                        content += lines
                        for row, line in enumerate(map_roads):
                            lines = str(row + 1) + "\t\t" + "\t\t" + str(line[4]) + "\t\t" + \
                                    str(line[5]) + "\t\t" + str(line[0]) + "\t\t" + str(line[1]) + "\t\t" + \
                                    str(line[2]) + "\t\t" + str(line[3]) + "\n"
                            content += lines
                        if content:
                            with open(reply[0], "a", encoding="utf-8") as f:
                                f.writelines(content)
                            QMessageBox.information(self, "提示", "导出成功")
                            self.textBrowser_inter.clear()
                            self.textBrowser_inter.append("数据导出成功，保存文件为：{}".format(reply[0]))
                        else:
                            QMessageBox.information(self, "提示", "导出失败")
                            self.textBrowser_inter.clear()
                            self.textBrowser_inter.append("数据导出失败！")
                except (IndexError, Exception):
                    pass
            else:
                QMessageBox.about(self, '提示', '暂无数据可以导出，请先进行选点操作')
                self.textBrowser_inter.clear()
                self.textBrowser_inter.append("数据导出失败！")
        else:
            QMessageBox.about(self, '提示', '请先切换至选点模式')

    # 打开本地帮助文档
    def open_local_help_chm(self):
        path = r".\resource\help\help.CHM"
        if os.path.exists(path):
            # 运行为windows平台
            if sys.platform == 'win32':
                os.startfile(path)
            # 运行为linux平台
            else:
                if 'XDG_CURRENT_DESKTOP' in os.environ:
                    opener = ['xdg-open']
                elif 'EDITOR' in os.environ:
                    opener = [os.environ['EDITOR']]
                else:
                    opener = ['vi']
                opener.append(path)
                subprocess.call(opener)
            self.textBrowser_inter.clear()
            info = "打开本地帮助CHM文档，文档地址为{}".format(path)
            self.textBrowser_inter.append(info)
        else:
            QMessageBox.warning(self, "提示", "当前目录下帮助文档不存在")
            self.textBrowser_inter.clear()
            info = "本地帮助CHM文档不存在，请将帮助文档复制到当前目录下的{}".format(path)
            self.textBrowser_inter.append(info)

    # 网络在线帮助
    @staticmethod
    def open_online_help_network():
        webbrowser.open("https://www.baidu.com/")

    # 显示界面交互信息
    def show_inter_info(self, info):
        self.textBrowser_inter.clear()
        self.textBrowser_inter.append(info)

    # 显示道路信息
    def show_road_info(self, info):
        self.textBrowser_road.clear()
        for row, line in enumerate(info):
            self.textBrowser_road.append(line)

    # 配置地图信息完成
    def set_map_info(self, info):
        self.map_label.line_width = info[0]
        self.map_label.crowded_level = info[1]
        self.map_canvas()
        self.map_mode()

    # 配置数据库信息完成
    def set_sql_info(self, info):
        if use_database:
            self.DB.connect_mysql(info)
            self.get_road_data()

    # 撤销操作
    def undo_operation(self):
        if self.map_label.operation_record:
            last_operation = self.map_label.operation_record.pop()
            self.undo_operation_record.append(last_operation)
            # 撤销交叉口选点
            if last_operation[0] == 0:
                map_nodes.pop()
                # 调出最近的起点选择操作
                for row, line in enumerate(self.map_label.operation_record[::-1]):
                    # 存在起点
                    if len(line[1]) == 2:
                        self.map_label.start_point = True
                        self.map_label.end_point = False
                        break
                    else:
                        continue
                self.textBrowser_inter.clear()
                info = "撤销最近的一个交叉口选点操作！"
                self.textBrowser_inter.append(info)
            # 撤销道路起点选择
            elif last_operation[0] == 1:
                self.map_label.x1 = 0
                self.map_label.y1 = 0
                self.map_label.start_point = False
                self.map_label.end_point = True
                self.textBrowser_inter.clear()
                info = "撤销最近的一条道路起点选择操作！"
                self.textBrowser_inter.append(info)
            # 撤销单向道路连接
            elif last_operation[0] == 2:
                map_roads.pop()
                # 取消终点
                self.map_label.x2, self.map_label.y2 = 0, 0
                # 调出最近的起点选择操作
                for row, line in enumerate(self.map_label.operation_record[::-1]):
                    # 存在起点
                    if len(line[1]) == 2:
                        self.map_label.x1 = line[1][0]
                        self.map_label.y1 = line[1][1]
                        self.map_label.start_point = True
                        self.map_label.end_point = False
                        break
                    else:
                        continue
                self.textBrowser_inter.clear()
                info = "撤销最近的一次单向道路连接操作！"
                self.textBrowser_inter.append(info)
            # 撤销双向道路连接
            elif last_operation[0] == 3:
                map_roads.pop()
                map_roads.pop()
                # 取消终点
                self.map_label.x2, self.map_label.y2 = 0, 0
                # 调出最近的起点选择操作
                for row, line in enumerate(self.map_label.operation_record[::-1]):
                    # 存在起点
                    if len(line[1]) == 2:
                        self.map_label.x1 = line[1][0]
                        self.map_label.y1 = line[1][1]
                        self.map_label.start_point = True
                        self.map_label.end_point = False
                        break
                    else:
                        continue
                self.textBrowser_inter.clear()
                info = "撤销最近的一次双向道路连接操作！"
                self.textBrowser_inter.append(info)
        else:
            QMessageBox.warning(self, "警告", "现在没有记录可以撤销")

    # 恢复操作
    def do_operation(self):
        if self.undo_operation_record:
            last_undo_operation = self.undo_operation_record.pop()
            self.map_label.operation_record.append(last_undo_operation)
            # 恢复交叉口选点
            if last_undo_operation[0] == 0:
                map_nodes.append(last_undo_operation[1])
                self.textBrowser_inter.clear()
                info = "恢复最近的一个交叉口选点操作！"
                self.textBrowser_inter.append(info)
            # 恢复道路起点选择
            elif last_undo_operation[0] == 1:
                self.map_label.x1 = last_undo_operation[1][0]
                self.map_label.y1 = last_undo_operation[1][1]
                self.map_label.start_point = True
                self.map_label.end_point = False
                self.textBrowser_inter.clear()
                info = "恢复最近的一条道路起点选择操作！"
                self.textBrowser_inter.append(info)
            # 恢复单向道路连接
            elif last_undo_operation[0] == 2:
                self.map_label.x1 = 0
                self.map_label.y1 = 0
                self.map_label.start_point = False
                self.map_label.end_point = True
                map_roads.append(last_undo_operation[1])
                self.textBrowser_inter.clear()
                info = "恢复最近的一次单向道路连接操作！"
                self.textBrowser_inter.append(info)
            # 恢复双向道路连接
            elif last_undo_operation[0] == 3:
                self.map_label.x1 = 0
                self.map_label.y1 = 0
                self.map_label.start_point = False
                self.map_label.end_point = True
                map_roads.append(last_undo_operation[1])
                map_roads.append(last_undo_operation[2])
                self.textBrowser_inter.clear()
                info = "恢复最近的一次双向道路连接操作！"
                self.textBrowser_inter.append(info)
        else:
            QMessageBox.warning(self, "警告", "现在没有记录可以恢复")

    # 清除所有选点
    def clear_all_choose(self):
        reply = QMessageBox.warning(self, '警告', '即将清除当前全部选点数据，是否继续清除',
                                    QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
        if reply == QMessageBox.Yes:
            global map_nodes, map_roads
            map_nodes = []
            map_roads = []
            self.map_label.road_info = []
            self.map_label.operation_record = []
            self.undo_operation_record = []
            self.textBrowser_inter.clear()
            self.textBrowser_road.clear()
            self.textBrowser_inter.append("清除所有选点数据")

    # 切换地图模式
    def switch_map_mode(self):
        global map_mode
        if map_mode == "realtime_condition":
            map_mode = "choose_point"
            self.textBrowser_inter.clear()
            self.textBrowser_inter.append("道路选点模式：\n鼠标左键选择路口\n鼠标右键依次选择道路起点、终点")
            self.textBrowser_road.clear()
            self.map_mode()
        else:
            reply = QMessageBox.warning(self, '警告', '即将丢失当前选点数据，请确保已经导出正确的数据，是否继续退出',
                                        QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
            if reply == QMessageBox.Yes:
                map_mode = "realtime_condition"
                self.map_label.road_info = []
                self.textBrowser_inter.clear()
                self.textBrowser_inter.append("实时路况模式：\n鼠标左键按下查看坐标\n鼠标右键按下查看坐标")
                self.textBrowser_road.clear()
                self.map_mode()

    # 设置地图画布
    def map_canvas(self):
        self.map_label.resize(648, 543)
        self.map_light_label.resize(648, 543)
        self.map_label.setPixmap(QPixmap(self.map_bg))
        self.map_light_label.setPixmap(QPixmap(self.map_bg))
        self.map_label.setScaledContents(True)
        self.map_light_label.setScaledContents(True)

    # 设置地图模式
    def map_mode(self):
        global map_mode, map_nodes, map_roads
        # 实时路况模式
        if map_mode == "realtime_condition":
            # 设置控件区域鼠标的光标形状为箭头
            self.map_label.setCursor(Qt.ArrowCursor)
            # 清空道路信息框
            self.textBrowser_road.clear()
            # 设置实时路况模式下按钮可见
            self.groupBox_choose.setVisible(True)
            self.pushButton_choose.setText("道路选点")
            self.pushButton_choose.setEnabled(True)
            self.pushButton_clear_all.setEnabled(False)
            self.pushButton_do.setEnabled(False)
            self.pushButton_undo.setEnabled(False)
            # 读取数据
            self.get_road_data()
        # 道路选点模式
        else:
            # 设置控件区域鼠标的光标形状为十字形
            self.map_label.setCursor(Qt.CrossCursor)
            # 设置道路选点模式下按钮可见
            self.groupBox_choose.setVisible(True)
            self.pushButton_choose.setText("实时路况")
            self.pushButton_choose.setEnabled(True)
            self.pushButton_clear_all.setEnabled(True)
            self.pushButton_do.setEnabled(True)
            self.pushButton_undo.setEnabled(True)
            # 清空数据
            map_nodes = []
            map_roads = []

    # 获取道路数据
    def get_road_data(self):
        global map_nodes, map_roads, map_threshold, area_cross, area_cross_phase, area_cross_road
        # 清空数据
        map_nodes = []
        map_roads = []
        map_threshold = []
        if use_database:
            # 从数据库获取数据
            map_nodes = self.DB.query_map_nodes()
            map_roads = self.DB.query_map_roads()
            map_threshold = self.DB.query_map_threshold()
            if (not map_nodes) or (not map_roads):
                # 数据库数据不存在，自动从文件获取数据
                data = self.read_file_data()
                map_nodes, map_roads, map_threshold = data[0], data[1], data[2]
        else:
            # 从文件获取数据
            data = self.read_file_data()
            map_nodes, map_roads, map_threshold = data[0], data[1], data[2]
        area = []
        area_cross = {}
        area_cross_phase = {}
        area_cross_road = {}
        for row, line in enumerate(map_nodes):
            if line[1]:
                if line[2] not in area:
                    area.append(line[2])
                    area_cross[line[2]] = [line[3]]
                    area_cross_phase[line[2]] = [(line[3], line[4])]
                else:
                    area_cross[line[2]].append(line[3])
                    area_cross_phase[line[2]].append((line[3], line[4]))
        for row, line in enumerate(map_roads):
            # 区域不存在
            if line[1] not in area_cross_road.keys():
                area_cross_road[line[1]] = [(line[2], [line[0]])]
            # 区域存在
            else:
                # 路口存在
                for i, e in enumerate(area_cross_road[line[1]]):
                    if line[1] == e[0]:
                        area_cross_road[line[1]][i][1].append(line[0])
                        break
                # 路口不存在
                else:
                    area_cross_road[line[1]].append((line[2], [line[0]]))

    # 读取文件数据
    def read_file_data(self):
        data_path = "./resource/data/data.xlsx"
        nodes = []
        roads = []
        threshold = []
        try:
            import xlrd
            import datetime
            work_book = xlrd.open_workbook(data_path)
            sheets_name = work_book.sheet_names()
            for index in range(work_book.nsheets):
                sheet = work_book.sheet_by_name(sheets_name[index])
                sheets_name[index] = []
                for row in range(sheet.nrows):
                    line = []
                    for col in range(0, sheet.ncols):
                        """ 0 empty, 1 string, 2 number, 3 date, 4 boolean, 5 error """
                        if sheet.cell(row, col).ctype == 3:
                            # 保存为时间格式
                            date = xlrd.xldate_as_tuple(sheet.cell(row, col).value, 0)
                            line.append(str(datetime.time(*date[3::])))
                        elif sheet.cell(row, col).ctype == 2:
                            # 保存为整型
                            line.append(int(sheet.cell_value(row, col)))
                        else:
                            line.append(sheet.cell_value(row, col))
                    sheets_name[index].append(line)
            nodes = sheets_name[0][1::]
            roads = sheets_name[1][1::]
            threshold = sheets_name[2][1::]
            self.textBrowser_inter.clear()
            info = "查询数据库失败，程序从文件中加载数据，文件地址为{}".format(data_path)
            self.textBrowser_inter.append(info)
        except (IndexError, Exception):
            self.textBrowser_inter.clear()
            info = "查询数据库失败，从文件中加载数据仍然失败，文件地址为{}".format(data_path)
            self.textBrowser_inter.append(info)
        return [nodes, roads, threshold]

    # 车流量转换为拥堵等级
    @staticmethod
    def flow_convert_crowd(flow):
        crowd = []
        for i in range(len(flow)):
            crowd.append(0)
        for i in range(len(flow)):
            for row, line in enumerate(map_threshold):
                if line[2] == flow[i][0]:
                    G1, G2, G3 = line[-3], line[-2], line[-1]
                    if flow[i][1] < G1:
                        crowd[i] = 0
                    elif G1 <= flow[i][1] < G2:
                        crowd[i] = 1
                    elif G2 <= flow[i][1] < G3:
                        crowd[i] = 2
                    else:
                        crowd[i] = 3
                    break
        return crowd

    # 显示道路状况
    def show_road_condition(self):
        global road_crowd_data, road_flow_data, map_roads, use_database
        if map_mode == "realtime_condition":
            if use_database:
                # 利用数据库查询道路拥堵程度得分
                info = ["20210227", "08:00:00", 0, 0, 0]
                road_flow_data = self.DB.query_roads_flow(info)
                road_crowd_data = self.flow_convert_crowd(road_flow_data)
            else:
                # 利用随机数生成拥堵程度分级得分
                road_flow_data = []
                for i in range(len(map_roads)):
                    road_flow_data.append([i + 1, random.randint(30, 300)])
                road_crowd_data = self.flow_convert_crowd(road_flow_data)
            # 数据不存在
            if not road_crowd_data:
                self.textBrowser_road.clear()
                info = "数据库查询道路拥堵程度失败"
                self.textBrowser_road.append(info)
            else:
                # 显示当前拥堵程度分数
                self.textBrowser_road.clear()
                for i in range(len(road_crowd_data)):
                    self.textBrowser_road.append("道路: {:>02d} -> 车流量: {:<3d} -> 拥堵等级: {:<1d}".
                                                 format(i + 1, road_flow_data[i][1], road_crowd_data[i]))
                self.textBrowser_road.moveCursor(QTextCursor.Start)

    # 初始化信号配时界面
    def init_light(self):
        # 三色信号灯样式表
        self.qss_style = [
            # 红灯
            """
            QTextEdit{
                border:none;
                color:black;
                font-size:11;
                border-radius:50px;
                padding-left:10px;
                padding-right:10px;
                text-align:middle;
                background:LightGray;
                background-color: rgb(255, 0, 0); 
            }
            """,
            # 绿灯
            """
            QTextEdit{
                border:none;
                color:black;
                font-size:11;
                border-radius:50px;
                padding-left:10px;
                padding-right:10px;
                text-align:middle;
                background:LightGray;
                background-color: rgb(0, 255, 0);
            }
            """,
            # 黄灯
            """
            QTextEdit{
                border:none;
                color:black;
                font-size:11;
                border-radius:50px;
                padding-left:10px;
                padding-right:10px;
                text-align:middle;
                background:LightGray;
                background-color: rgb(255, 255, 0);
            }
            """,
            # 熄灭
            """
            QTextEdit{
                border:none;
                color:black;
                font-size:11;
                border-radius:50px;
                padding-left:10px;
                padding-right:10px;
                text-align:middle;
                background:LightGray;
                background-color: rgb(190, 190, 190);
            }
            """,
        ]
        # 让图片自适应label大小
        self.map_light_label.setScaledContents(True)
        # 设置lcd背景色
        self.lcdNumber.setStyleSheet("background-color: black")
        # 设置下拉框选项
        self.load_area_cross_phase_combobox(1, self.comboBox2_1, self.comboBox2_2, self.comboBox2_3, 1, 0, 0)
        # 启动定时器
        self.timer_light.start(1000)
        # 获取当前倒计时、当前预测配时
        self.get_count_time()
        # 槽函数连接
        self.timer_light.timeout.connect(self.show_light_time)
        self.comboBox2_1.currentIndexChanged.connect(self.select_area_combobox)
        self.comboBox2_2.currentIndexChanged.connect(self.select_cross_combobox)
        self.comboBox2_3.currentIndexChanged.connect(self.get_count_time)

    # 选中区域下拉框
    def select_area_combobox(self):
        page = self.tabWidget.currentIndex()
        if page == 1:
            self.load_cross_combobox(page, self.comboBox2_1, self.comboBox2_2)
            self.get_count_time()
        elif page == 2:
            self.load_cross_combobox(page, self.comboBox3_3, self.comboBox3_4)
        elif page == 3:
            self.load_cross_combobox(page, self.comboBox4_3, self.comboBox4_4)

    # 选中路口下拉框
    def select_cross_combobox(self):
        page = self.tabWidget.currentIndex()
        if page == 1:
            self.load_phase_combobox(page, self.comboBox2_1, self.comboBox2_2, self.comboBox2_3)
            self.get_count_time()
        elif page == 2:
            self.load_phase_combobox(page, self.comboBox3_3, self.comboBox3_4, self.comboBox3_5)
        elif page == 3:
            self.load_phase_combobox(page, self.comboBox4_3, self.comboBox4_4, self.comboBox4_5)

    # 加载路口下拉框选项
    def load_cross_combobox(self, page, box1, box2):
        global map_nodes
        box2.currentIndexChanged.disconnect(self.select_cross_combobox)
        if page == 1:
            area_number = int(box1.currentText()[2::])
            box2.clear()
            for row, line in enumerate(area_cross[area_number]):
                box2.addItem("路口" + str(line))
            box2.currentIndexChanged.connect(self.select_cross_combobox)
            box2.setCurrentIndex(0)
            self.select_cross_combobox()
        elif page == 2 or page == 3:
            area_index = int(box1.currentIndex())
            cross_index = int(box2.currentIndex())
            area_text = box1.currentText()
            cross_text = box2.currentText()
            # 区域编号不为0，即区域未变为"全部"
            if area_index != 0:
                area_number = int(area_text[2::])
                box2.clear()
                box2.addItem("全部")
                all_cross_number = []
                for row, line in enumerate(area_cross[area_number]):
                    all_cross_number.append(line)
                    box2.addItem("路口" + str(line))
                box2.currentIndexChanged.connect(self.select_cross_combobox)
                # 原路口编号不为0，即原路口未选中"全部"
                if cross_index != 0:
                    cross_number = int(cross_text[2::])
                    for i in range(len(all_cross_number)):
                        if all_cross_number[i] == cross_number:
                            box2.setCurrentIndex(i + 1)
                            break
                    else:
                        box2.setCurrentIndex(0)
                else:
                    box2.setCurrentIndex(0)
                self.select_cross_combobox()
            # 区域编号为0，即区域变为"全部"
            else:
                box2.clear()
                box2.addItem("全部")
                all_cross_number = []
                for value in area_cross.values():
                    for i, element in enumerate(value):
                        if element not in all_cross_number:
                            all_cross_number.append(element)
                            box2.addItem("路口" + str(element))
                box2.currentIndexChanged.connect(self.select_cross_combobox)
                # 原路口编号不为0，即原路口未选中"全部"
                if cross_index != 0:
                    cross_number = int(cross_text[2::])
                    if cross_number in all_cross_number:
                        for i in range(len(all_cross_number) + 1):
                            if box2.itemText(i) == "路口" + str(cross_number):
                                box2.setCurrentIndex(i)
                                break
                    else:
                        box2.setCurrentIndex(0)
                else:
                    box2.setCurrentIndex(0)
                self.select_cross_combobox()

    # 加载相位下拉框选项
    def load_phase_combobox(self, page, box1, box2, box3):
        if page == 1:
            box3.currentIndexChanged.disconnect(self.get_count_time)
            area_number = int(box1.currentText()[2::])
            cross_number = int(box2.currentText()[2::])
            phase_index = int(box3.currentIndex())
            box3.clear()
            for row, line in enumerate(area_cross_phase[area_number]):
                if line[0] == cross_number:
                    phase_amount = line[1]
                    for i in range(phase_amount):
                        box3.addItem("相位" + str(i + 1))
                    if phase_amount > phase_index >= 0:
                        box3.setCurrentIndex(phase_index)
                    else:
                        box3.setCurrentIndex(0)
                    break
            box3.currentIndexChanged.connect(self.get_count_time)
        # 相位变为路段
        elif page == 2 or page == 3:
            area_index = int(box1.currentIndex())
            cross_index = int(box2.currentIndex())
            area_text = box1.currentText()
            cross_text = box2.currentText()
            # 区域、路口编号均不为0，即区域、路口未变为"全部"
            if area_index != 0 and cross_index != 0:
                area_number = int(area_text[2::])
                cross_number = int(cross_text[2::])
                box3.clear()
                box3.addItem("全部")
                all_road_number = []
                for row, line in enumerate(area_cross_road[area_number]):
                    if line[0] == cross_number:
                        for i in range(len(line[1])):
                            all_road_number.append(line[1][i])
                all_road_number.sort()
                for i in range(len(all_road_number)):
                    box3.addItem("路段" + str(all_road_number[i]))
                box3.setCurrentIndex(0)
            # 区域编号为0，即区域变为"全部", 路口编号不为0，即路口不变为"全部"
            elif area_index == 0 and cross_index != 0:
                cross_number = int(cross_text[2::])
                box3.clear()
                box3.addItem("全部")
                all_road_number = []
                for value in area_cross_road.values():
                    for i, element in enumerate(value):
                        if element[0] == cross_number:
                            for j in range(len(element[1])):
                                all_road_number.append(element[1][j])
                all_road_number.sort()
                for i in range(len(all_road_number)):
                    box3.addItem("路段" + str(all_road_number[i]))
                box3.setCurrentIndex(0)
            # 区域编号不为0，即区域不变为"全部", 路口编号为0，即路口变为"全部"
            elif area_index != 0 and cross_index == 0:
                area_number = int(area_text[2::])
                box3.clear()
                box3.addItem("全部")
                all_road_number = []
                for row, line in enumerate(area_cross_road[area_number]):
                    for i in range(len(line[1])):
                        all_road_number.append(line[1][i])
                all_road_number.sort()
                for i in range(len(all_road_number)):
                    box3.addItem("路段" + str(all_road_number[i]))
                box3.setCurrentIndex(0)
            # 区域编号为0，即区域变为"全部", 路口编号为0，即路口变为"全部"
            elif area_index == 0 and cross_index == 0:
                box3.clear()
                box3.addItem("全部")
                for i in range(len(map_roads)):
                    box3.addItem("路段" + str(i + 1))
                box3.setCurrentIndex(0)

    # 加载区域、路口、路段下拉框选项
    @staticmethod
    def load_area_cross_phase_combobox(page, box1, box2, box3, idx1, idx2, idx3):
        global area_cross, area_cross_phase, area_cross_road, map_nodes, map_roads
        box1.clear()
        box2.clear()
        box3.clear()
        if page == 1:
            for key in area_cross.keys():
                box1.addItem("区域" + str(key))
            box1.setCurrentIndex(idx1)
            area_number = int(box1.currentText()[2::])
            for row, line in enumerate(area_cross[area_number]):
                box2.addItem("路口" + str(line))
            box2.setCurrentIndex(idx2)
            cross_number = int(box2.currentText()[2::])
            for row, line in enumerate(area_cross_phase[area_number]):
                if line[0] == cross_number:
                    for i in range(line[1]):
                        box3.addItem("相位" + str(i + 1))
            box3.setCurrentIndex(idx3)
        elif page == 2 or page == 3:
            box1.addItem("全部")
            box2.addItem("全部")
            box3.addItem("全部")
            for key in area_cross.keys():
                box1.addItem("区域" + str(key))
            box1.setCurrentIndex(idx1)
            all_cross_number = []
            for value in area_cross.values():
                for i, element in enumerate(value):
                    if element not in all_cross_number:
                        all_cross_number.append(element)
                        box2.addItem("路口" + str(element))
            box2.setCurrentIndex(idx2)
            for i in range(len(map_roads)):
                box3.addItem("路段" + str(i + 1))
            box3.setCurrentIndex(idx3)

    # 汇总倒计时查询信息
    def collect_count_down_info(self):
        area = self.comboBox2_1.currentIndex() + 1
        cross = self.comboBox2_2.currentIndex() + 1
        phase = self.comboBox2_3.currentIndex() + 1
        return [area, cross, phase]

    # 获取当前倒计时信息
    def get_count_down_time(self):
        global use_database
        if use_database:
            # 汇总查询信息
            info = self.collect_count_down_info()
            if info[-1] > 0:
                # 通过数据库获取当前倒计时
                data = self.DB.query_count_down_time(info)
                if data:
                    result = data[0]
                else:
                    result = [10, 10, 10]
            else:
                result = []
        else:
            # 调试用：随机生成当前倒计时
            result = [10, 10, 10]
            result[0] = random.randint(2, 4)
            result[1] = random.randint(2, 4)
            result[2] = random.randint(2, 3)
        return result

    # 获取预测配时信息
    def get_pre_count_down_time(self):
        global use_database
        if use_database:
            # 汇总查询信息
            info = self.collect_count_down_info()
            if info[-1] > 0:
                # 通过数据库获取下一周期的预测配时
                data = self.DB.query_pre_count_down_time(info)
                if data:
                    result = data[0]
                else:
                    result = [10, 10, 10]
            else:
                result = []
        else:
            # 调试用：随机生成下一周期的预测配时
            result = [10, 10, 10]
            result[0] = random.randint(2, 4)
            result[1] = random.randint(2, 4)
            result[2] = random.randint(2, 3)
        return result

    # 获取最新倒计时和预测配时
    def get_count_time(self):
        area_number = int(self.comboBox2_1.currentText()[2::])
        cross_number = int(self.comboBox2_2.currentText()[2::])
        for row, line in enumerate(map_nodes):
            if line[1] and line[2] == area_number and line[3] == cross_number:
                self.map_light_label.location_x = line[-2]
                self.map_light_label.location_y = line[-1]
                self.map_light_label.update()
        # 获取当前倒计时
        self.count_down_time = self.get_count_down_time()
        # 获取当前预测配时
        self.pre_count_down_time = self.get_pre_count_down_time()
        if self.count_down_time and self.pre_count_down_time:
            self.timer_light.start()
            self.label_hint.setVisible(False)
            self.label_count_down.setVisible(True)
            self.label_pre_count_down.setVisible(True)
            self.label_pre_count.setVisible(True)
            self.label_count.setVisible(True)
            # 显示当前倒计时
            R, G, Y = self.count_down_time[0], self.count_down_time[1], self.count_down_time[2]
            self.label_count_down.setText("红: {}，绿: {}，黄: {}".format(R, G, Y))
            # 显示预测配时
            r, g, y = self.pre_count_down_time[0], self.pre_count_down_time[1], self.pre_count_down_time[2]
            self.label_pre_count_down.setText("红: {}，绿: {}，黄: {}".format(r, g, y))
            # 立即显示信号灯时间
            self.show_light_time()
        else:
            self.timer_light.stop()
            self.label_hint.setVisible(True)
            self.label_count_down.setVisible(False)
            self.label_pre_count_down.setVisible(False)
            self.label_pre_count.setVisible(False)
            self.label_count.setVisible(False)
            # 显示熄灭状态
            color = -1
            # 加载信号灯
            self.textEdit.setStyleSheet(self.qss_style[color])
            # 设置显示内容
            self.lcdNumber.display("%02d" % 0)
            # 设置显示风格
            self.lcdNumber.setSegmentStyle(QLCDNumber.Flat)
            # 设置数字颜色
            pat = self.lcdNumber.palette()
            pat.setColor(QPalette.Normal, QPalette.WindowText, Qt.gray)
            self.lcdNumber.setPalette(pat)

    # 显示信号灯色及倒计时信息
    def show_light_time(self):
        # 黄灯倒计时为0，加载预测配时
        if self.count_down_time[2] == 0:
            # 加载下一周期的预测配时为当前倒计时
            self.count_down_time[0] = self.pre_count_down_time[0]
            self.count_down_time[1] = self.pre_count_down_time[1]
            self.count_down_time[2] = self.pre_count_down_time[2]
            # 获取下一周期的预测配时
            self.pre_count_down_time = self.get_pre_count_down_time()
            r = self.pre_count_down_time[0]
            g = self.pre_count_down_time[1]
            y = self.pre_count_down_time[2]
            self.label_pre_count_down.setText("红: {}，绿: {}，黄: {}".format(r, g, y))
        R, G, Y = self.count_down_time[0], self.count_down_time[1], self.count_down_time[2]
        self.label_count_down.setText("红: {}，绿: {}，黄: {}".format(R, G, Y))
        # 红色倒计时不为0
        if self.count_down_time[0] != 0:
            self.count_down_time[0] -= 1
            # 显示红灯
            color = 0
            # 设置显示内容
            self.lcdNumber.display("%02d" % R)
            # 设置显示风格
            self.lcdNumber.setSegmentStyle(QLCDNumber.Flat)
            # 设置数字颜色
            pat = self.lcdNumber.palette()
            pat.setColor(QPalette.Normal, QPalette.WindowText, Qt.red)
            self.lcdNumber.setPalette(pat)
        # 红色倒计时为0，绿色倒计时不为0
        elif self.count_down_time[1] != 0:
            self.count_down_time[1] -= 1
            # 显示绿灯
            color = 1
            # 设置显示内容
            self.lcdNumber.display("%02d" % G)
            # 设置显示风格
            self.lcdNumber.setSegmentStyle(QLCDNumber.Flat)
            # 设置数字颜色
            pat = self.lcdNumber.palette()
            pat.setColor(QPalette.Normal, QPalette.WindowText, Qt.green)
            self.lcdNumber.setPalette(pat)
        # 红色倒计时为0，绿色倒计时为0，黄色倒计时不为0
        elif self.count_down_time[2] != 0:
            self.count_down_time[2] -= 1
            # 显示黄灯
            color = 2
            # 设置显示内容
            self.lcdNumber.display("%02d" % Y)
            # 设置显示风格
            self.lcdNumber.setSegmentStyle(QLCDNumber.Flat)
            # 设置数字颜色
            pat = self.lcdNumber.palette()
            pat.setColor(QPalette.Normal, QPalette.WindowText, Qt.yellow)
            self.lcdNumber.setPalette(pat)
        # 红色倒计时为0，绿色倒计时为0，黄色倒计时为0
        else:
            # 显示红灯
            color = 0
        # 刷新灯色
        self.textEdit.setStyleSheet(self.qss_style[color])

    # 初始化状态界面
    def init_status(self):
        # 创建日期时间空间，并把当前日期时间赋值
        self.dateEdit.setDisplayFormat('yyyy-MM-dd')
        self.dateEdit_2.setDisplayFormat('yyyy-MM-dd')
        # # 设置日期最大值与最小值，在当前日期的基础上，后一年与前一年
        self.dateEdit.setMinimumDate(QDate.currentDate().addDays(-365))
        self.dateEdit_2.setMinimumDate(QDate.currentDate().addDays(-365))
        self.dateEdit.setMaximumDate(QDate.currentDate().addDays(365))
        self.dateEdit_2.setMaximumDate(QDate.currentDate().addDays(365))
        # 设置为当前日期
        self.dateEdit.setDate(QDate.currentDate())
        self.dateEdit_2.setDate(QDate.currentDate())
        # 设置日历控件允许弹出
        self.dateEdit.setCalendarPopup(True)
        self.dateEdit_2.setCalendarPopup(True)
        # 设置表格内容不可编辑
        self.tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.tableWidget_2.setEditTriggers(QAbstractItemView.NoEditTriggers)
        # 设置表头样式
        self.tableWidget.horizontalHeader().setStyleSheet(
            "QHeaderView::section{background-color:rgb(155, 194, 230);font:10pt '宋体';color: black;};")
        self.tableWidget_2.horizontalHeader().setStyleSheet(
            "QHeaderView::section{background-color:rgb(155, 194, 230);font:10pt '宋体';color: black;};")
        # 设置表格水平充满
        self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.tableWidget_2.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        # 设置下拉框选项
        self.load_area_cross_phase_combobox(2, self.comboBox3_3, self.comboBox3_4, self.comboBox3_5, 0, 0, 0)
        self.load_area_cross_phase_combobox(3, self.comboBox4_3, self.comboBox4_4, self.comboBox4_5, 0, 0, 0)
        # 信息查询改变信号槽函数连接
        self.comboBox3_3.currentIndexChanged.connect(self.select_area_combobox)
        self.comboBox4_3.currentIndexChanged.connect(self.select_area_combobox)
        self.comboBox3_4.currentIndexChanged.connect(self.select_cross_combobox)
        self.comboBox4_4.currentIndexChanged.connect(self.select_cross_combobox)
        # 查找功能
        self.pushButton3_1.clicked.connect(self.show_traffic_state)
        self.pushButton3_2.clicked.connect(self.reset_traffic_state)
        self.pushButton4_1.clicked.connect(self.show_work_state)
        self.pushButton4_2.clicked.connect(self.reset_work_state)
        # 按钮图标设置
        self.pushButton3_1.setIcon(QIcon(QPixmap("./resource/icon/search.png")))
        self.pushButton3_2.setIcon(QIcon(QPixmap("./resource/icon/reset.png")))
        self.pushButton4_1.setIcon(QIcon(QPixmap("./resource/icon/search.png")))
        self.pushButton4_2.setIcon(QIcon(QPixmap("./resource/icon/reset.png")))

    # 切换到电子地图界面
    def switch_to_map_panel(self):
        global map_show
        self.tabWidget.setCurrentIndex(0)
        self.statusBar().showMessage("")
        map_show = True
        self.timer_map.start(1000)

    # 切换到信号灯配时界面
    def switch_to_light_time_panel(self):
        global map_show
        self.tabWidget.setCurrentIndex(1)
        self.statusBar().showMessage("")
        map_show = False
        self.timer_map.stop()

    # 切换到交通状态界面·
    def switch_to_traffic_state_panel(self):
        global map_show
        self.tabWidget.setCurrentIndex(2)
        self.statusBar().showMessage("")
        map_show = False
        self.timer_map.stop()

    # 切换到工作状态界面
    def switch_to_work_state_panel(self):
        global map_show
        self.tabWidget.setCurrentIndex(3)
        self.statusBar().showMessage("")
        map_show = False
        self.timer_map.stop()

    # 重置交通状态
    def reset_traffic_state(self):
        self.dateEdit.setDate(QDate.currentDate())
        self.comboBox3_1.setCurrentIndex(8)
        self.comboBox3_2.setCurrentIndex(0)
        self.comboBox3_3.setCurrentIndex(0)
        self.comboBox3_4.setCurrentIndex(0)
        self.comboBox3_5.setCurrentIndex(0)
        # 清空查询结果表
        self.tableWidget.setRowCount(0)
        self.tableWidget.clearContents()

    # 重置工作状态
    def reset_work_state(self):
        self.dateEdit_2.setDate(QDate.currentDate())
        self.comboBox4_1.setCurrentIndex(8)
        self.comboBox4_2.setCurrentIndex(0)
        self.comboBox4_3.setCurrentIndex(0)
        self.comboBox4_4.setCurrentIndex(0)
        self.comboBox4_5.setCurrentIndex(0)
        # 清空查询结果表
        self.tableWidget_2.setRowCount(0)
        self.tableWidget_2.clearContents()

    # 汇总交通状态查询信息
    @staticmethod
    def collect_status_query_info(date_edit, box1, box2, box3, box4, box5):
        # 获取查询条件
        date = date_edit.date()  # 日期
        date = date.toString(Qt.ISODate)  # 转换为字符串
        date = date[:4] + date[5:7] + date[8:]  # 去除连字符
        hour = box1.currentText()  # 小时
        minute = box2.currentText()  # 分钟
        area = box3.currentIndex()  # 区域
        cross = box4.currentIndex()  # 路口
        road = box5.currentIndex()  # 路段
        if area != 0:
            area = int(box3.currentText()[2::])
        if cross != 0:
            cross = int(box4.currentText()[2::])
        if road != 0:
            road = int(box5.currentText()[2::])
        time = hour + ":" + minute + ":" + "00"
        return [date, time, area, cross, road]

    # 通过表格显示数据库状态查询结果
    def show_status_result(self, query, info, table):
        # 通过数据库查询结果
        if query == 0:
            data = self.DB.query_traffic_status(info)
        else:
            data = self.DB.query_work_status(info)
        # 通过表格显示查询结果
        if data:
            table.setRowCount(0)
            table.insertRow(0)
            row_position = 1
            for row, line in enumerate(data):
                for column, element in enumerate(line):
                    newItem = QTableWidgetItem(str(element))
                    if element != "异常":
                        newItem.setForeground(QBrush(QColor(0, 0, 0)))
                    else:
                        newItem.setForeground(QBrush(QColor(255, 0, 0)))
                    table.setItem(row, column, newItem)
                    # 水平、垂直居中
                    table.item(row, column).setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
                if row < len(data) - 1:
                    table.insertRow(row_position)
                    row_position += 1
            # 状态栏输出结果
            result = "查询完成 共查询到 {0} 条结果".format(len(data))
            self.statusBar().showMessage(" " * 5 + result)
        # 状态栏输出结果
        else:
            result = "查询完成 共查询到 {0} 条结果".format(0)
            self.statusBar().showMessage(" " * 5 + result)

    # 交通状态界面：查询对应信息的数据
    def show_traffic_state(self):
        # 首先清空查询结果表
        self.tableWidget.setRowCount(0)
        self.tableWidget.clearContents()
        # 获取查询条件
        info = self.collect_status_query_info(
            self.dateEdit, self.comboBox3_1, self.comboBox3_2,
            self.comboBox3_3, self.comboBox3_4, self.comboBox3_5)
        # 显示数据库查询结果
        self.show_status_result(0, info, self.tableWidget)

    # 工作状态界面：查询对应信息的数据
    def show_work_state(self):
        # 首先清空查询结果表
        self.tableWidget_2.setRowCount(0)
        self.tableWidget_2.clearContents()
        # 获取查询条件
        info = self.collect_status_query_info(
            self.dateEdit_2, self.comboBox4_1, self.comboBox4_2,
            self.comboBox4_3, self.comboBox4_4, self.comboBox4_5)
        # 显示数据库查询结果
        self.show_status_result(1, info, self.tableWidget_2)
